package com.ilia.util.timer.impl;

import java.util.*;

import com.ilia.util.timer.ITimer;

public class Timer implements ITimer {
	public static String nr = "\n";

	private Map<String, SingleTimer> timers;
	private Stack<SingleTimer> stack;

	public Timer() {
		timers = new HashMap<String, SingleTimer>();
		stack = new Stack<SingleTimer>();
	}

	public void startTimer(String timerName) {
		// if (timerName.equals("ALL")) {
		SingleTimer st = (SingleTimer) timers.get(timerName);
		if (st == null) {
			st = new SingleTimer(timerName);
			timers.put(timerName, st);
		}

		if (!stack.empty()) {
			SingleTimer parent = (SingleTimer) stack.peek();
			parent.startDelegate();
		}
		stack.push(st);
		st.start();
		// }
	};

	public void stopTimer(String timerName) {
		// if (timerName.equals("ALL")) {
		SingleTimer st = (SingleTimer) timers.get(timerName);
		if (st == null) {
			System.out.println("TIME DOES NOT EXISTS");
			return;
		}
		SingleTimer o = (SingleTimer) stack.peek();
		if (o != st) {
			System.out.println("WRONG SEQUENSE OF TIMERS! expected:" + o.name
					+ " requested: " + st.name);
			return;
		}
		stack.pop();
		if (!stack.empty()) {
			SingleTimer parent = (SingleTimer) stack.peek();
			parent.stopDelegate();
		}
		st.stop();
		// }
	};

	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Timers:");
		sb.append(nr);
		Iterator<String> it = timers.keySet().iterator();
		while (it.hasNext()) {
			String name = it.next();
			SingleTimer st = (SingleTimer) timers.get(name);
			sb.append(st.toString());

		}
		return sb.toString();

	}

	class SingleTimer {

		private long selfTime;
		private long delegatedTime;
		private long calls;

		private String name;

		public SingleTimer(String name) {
			this.name = name;
			this.selfTime = 0;
			this.delegatedTime = 0;
			this.calls = 0;
		}

		public long getSelfTime() {
			return selfTime;
		}

		public long getDelegatedTime() {
			return delegatedTime;
		}

		public long getTime() {
			return selfTime + delegatedTime;
		}

		public void addSelfTime(long timeToAdd) {
			this.selfTime += timeToAdd;
		}

		public void addDelegatedTime(long timeToAdd) {
			this.delegatedTime += timeToAdd;
		}

		private long startTime;
		private long delStartTime;

		public void start() {
			startTime = System.currentTimeMillis();
			calls++;
		};

		public void stop() {
			long t = System.currentTimeMillis() - startTime;
			addSelfTime(t);
		}

		public void startDelegate() {
			stop();
			delStartTime = System.currentTimeMillis();
		};

		public void stopDelegate() {
			start();
			calls--;
			long t = System.currentTimeMillis() - delStartTime;
			addDelegatedTime(t);
		}

		public String toString() {
			StringBuffer sb = new StringBuffer();
			sb.append("" + name + "=" + getSelfTime() + "+"
					+ getDelegatedTime() + "/" + calls + ".");
			sb.append(nr);

			return sb.toString();
		}

	}

}